package cc.shacocloud.mirage.starter;

import cc.shacocloud.mirage.bean.ConfigurableBeanFactory;
import cc.shacocloud.mirage.core.ConfigurableApplicationContext;
import cc.shacocloud.mirage.starter.banner.MirageBootBanner;
import cc.shacocloud.mirage.utils.ClassUtil;
import cc.shacocloud.mirage.utils.annotation.AnnotatedElementUtils;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.time.Duration;
import java.util.Objects;

/**
 * 应用启动类
 *
 * @author shaco
 */
@Slf4j
public class MirageApplication {
    
    /**
     * 应用类加载器
     */
    private final ClassLoader classLoader;
    
    /**
     * 应用启动类
     */
    private final Class<?> applicationClass;
    
    @Setter
    @Nullable
    private MirageBootBanner mirageBootBanner;
    
    public MirageApplication(ClassLoader classLoader, Class<?> applicationClass) {
        this.classLoader = classLoader;
        this.applicationClass = applicationClass;
        
        if (!AnnotatedElementUtils.hasAnnotation(applicationClass, MirageBootApplication.class)) {
            throw new RuntimeException(String.format("应用启动类 %s 上必须标识 @MirageBootApplication 注解！", applicationClass.getCanonicalName()));
        }
    }
    
    /**
     * 静态方法启动
     */
    public static ConfigurableApplicationContext run(Class<?> applicationClass, String... args) {
        return new MirageApplication(ClassUtil.getDefaultClassLoader(), applicationClass).runApp(args);
    }
    
    /**
     * 静态方法启动
     *
     * @param classLoader 应用类加载器
     */
    public static ConfigurableApplicationContext run(ClassLoader classLoader, Class<?> applicationClass, String... args) {
        return new MirageApplication(classLoader, applicationClass).runApp(args);
    }
    
    /**
     * 启动应用
     *
     * @param args 应用启动参数
     * @return {@link ConfigurableApplicationContext}
     */
    public ConfigurableApplicationContext runApp(String... args) {
        final long startupDate = System.nanoTime();
        
        // 打印 banner
        MirageBootBanner mirageBootBanner = doMirageBootBanner();
        mirageBootBanner.printBanner(System.out);
        
        // 启动信息日志
        StartupInfoLogger startupInfoLogger = new StartupInfoLogger(this.applicationClass);
        startupInfoLogger.logStarting(log);
        
        final ConfigurableApplicationContext applicationContext = doApplicationContext();
        final ConfigurableBeanFactory beanFactory = applicationContext.getBeanFactory();
        
        // 加载应用初始Bean
        beanFactory.registerScanClass(applicationClass);
        
        try {
            applicationContext.start();
            
            Duration startupDuration = Duration.ofNanos(System.nanoTime() - startupDate);
            startupInfoLogger.logStarted(log, startupDuration);
            
        } catch (Throwable cause) {
            if (log.isErrorEnabled()) {
                log.error("应用启动失败！", cause);
            }
            
            System.exit(-1);
        }
        
        return applicationContext;
    }
    
    /**
     * 获取当前应用使用的上下文对象
     */
    @NotNull
    protected ConfigurableApplicationContext doApplicationContext() {
        if (ClassUtil.isPresent("cc.shacocloud.mirage.restful.MirageRequestMappingHandler", classLoader)) {
            return new MirageRestfulApplicationContext(classLoader);
        }
        return new MirageApplicationContext(classLoader);
    }
    
    /**
     * 获取应用的 {@link MirageBootBanner}
     */
    @NotNull
    protected MirageBootBanner doMirageBootBanner() {
        if (Objects.isNull(this.mirageBootBanner)) {
            this.mirageBootBanner = new MirageBootBanner();
        }
        
        return this.mirageBootBanner;
    }
    
}
